/*
 * This file is part of the Sx Framework Library.
 * 
 * Copyright (C) 2013 University of Colorado Denver
 * <min.choi@ucdenver.edu> <shane.transue@ucdenver.edu>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy 
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
 * copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 * DEALINGS IN THE SOFTWARE.
 */
#ifndef SX_LINKED_LIST_H
#define SX_LINKED_LIST_H

/*
 * The implementation class of the LinkedList cannot be hidden in an opaque
 * pointer due to the template definition of the class; however the
 * implementation of the LinkedList interface can be facilitated through the
 * standard list container.
 */
#include <list>

namespace Sx {
namespace Util {

/*
 * This vector array class implements a basic list wrapper around a
 * standard list type.
 */
template <typename T, typename allocator = std::allocator<T> >
class LinkedList {
public:
	LinkedList();
	LinkedList(LinkedList<T, allocator>& list);
	~LinkedList();

	/*
	 * The following functions add a data item to the end of this List.
	 */
	void add(const T& data);
	void addLast(const T& data);
	void append(const T& data);

	/*
	 * The following functions add a data item to the beginning of this List.
	 */
	void prepend(const T& data);
	void addFirst(const T& data);

	/*
	 * The following functions return the first element contained in this List.
	 */
	const T& first() const;
	T getFirst() const;
	const T& head() const;

	/*
	 * The following functions return the last element contained in this List.
	 */
	const T& last() const;
	T getLast() const;
	const T& tail() const;

	/*
	 * Removes the first element in this List.
	 */
	void removeFirst();
	 
	/*
	 * Removes the last element in this List.
	 */
	void removeLast();

	/*
	 * The following functions removal all elements from this List.
	 */
	void empty();
	void clear();

	/*
	 * Determines if this List is empty. If this List is empty this function
	 * will return true; otherwise it will return false.
	 */
	bool isEmpty();
	
	/*
	 * Returns the number of elements in this List.
	 */
	unsigned int size() const;

	/*
	 * Assignment operator for copying a Stack.
	 */
	LinkedList<T, allocator>& operator = (const LinkedList<T, allocator>& l);

protected:
	typedef std::list<T, allocator> ListType;
	ListType imp;
};

template <typename T, typename allocator>
Sx::Util::LinkedList<T, allocator>::LinkedList() {
	this->imp = ListType();
}

template <typename T, typename allocator>
Sx::Util::LinkedList<T, allocator>::LinkedList(LinkedList<T, allocator>& list) {
	this->imp = list.imp;
}

template <typename T, typename allocator>
Sx::Util::LinkedList<T, allocator>::~LinkedList() { }


template <typename T, typename allocator>
void Sx::Util::LinkedList<T, allocator>::add(const T& data) {
	this->imp.push_back(data);
}

template <typename T, typename allocator>
void Sx::Util::LinkedList<T, allocator>::addLast(const T& data) {
	this->imp.push_back(data);
}

template <typename T, typename allocator>
void Sx::Util::LinkedList<T, allocator>::addFirst(const T& data) {
	this->imp.push_front(data);
}

template <typename T, typename allocator>
const T& Sx::Util::LinkedList<T, allocator>::first() const {
	return this->imp.front();
}

template <typename T, typename allocator>
T Sx::Util::LinkedList<T, allocator>::getFirst() const {
	return this->imp.front();
}

template <typename T, typename allocator>
const T& Sx::Util::LinkedList<T, allocator>::head() const {
	return this->imp.front();
}

template <typename T, typename allocator>
const T& Sx::Util::LinkedList<T, allocator>::last() const {
	return this->imp.back();
}

template <typename T, typename allocator>
T Sx::Util::LinkedList<T, allocator>::getLast() const {
	return this->imp.back();
}

template <typename T, typename allocator>
const T& Sx::Util::LinkedList<T, allocator>::tail() const {
	return this->imp.back();
}

template <typename T, typename allocator>
void Sx::Util::LinkedList<T, allocator>::removeFirst() {
	this->imp.pop_front();
}

template <typename T, typename allocator>
void Sx::Util::LinkedList<T, allocator>::removeLast() {
	this->imp.pop_back();
}

template <typename T, typename allocator>
void Sx::Util::LinkedList<T, allocator>::empty() {
	this->clear();
}

template <typename T, typename allocator>
void Sx::Util::LinkedList<T, allocator>::clear() {
	this->imp.erase(this->imp.begin(), this->imp.end());
}

template <typename T, typename allocator>
bool Sx::Util::LinkedList<T, allocator>::isEmpty() {
	if ( this->imp.size() == 0 ) return true;
	return false;
}

template <typename T, typename allocator>
unsigned int Sx::Util::LinkedList<T, allocator>::size() const {
	return this->imp.size();
}

template <typename T, typename allocator>
Sx::Util::LinkedList<T, allocator>& Sx::Util::LinkedList<T, allocator>::operator = (const LinkedList<T, allocator>& l) {
	if ( this == &l ) return *this;
	this->imp = l.imp;
	return *this;
}

}

}

#endif
